package br.edu.ufcg.unknown.sintatico;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import br.edu.ufcg.unknown.exception.UnknownException;
import br.edu.ufcg.unknown.lexico.Token;

/**
 * Responsável por guardar os Tokens encontrados. Suporta as operações de
 * <b>inserir</b> e <b>buscar</b>. A tentativa de inserir tokens cujo lexema
 * já foi adicionado antes implica na substituição do Token anterior.
 * 
 * @author Fábio Jorge
 * @author Jaindson Valentim
 * @author Moisés Rodrigues
 */
public class TabelaDeTokens {

	private Map<String, Token> palavrasReservadas = new HashMap<String, Token>();

	private Map<String, Token> identificadores = new HashMap<String, Token>();

	private int codigo = 1;

	/**
	 * Código a ser associado com tokens do tipo IDENTIFICADOR
	 */
	protected int codigo_t_ident;

	/**
	 * Código a ser associado com tokens do tipo NUM
	 */
	protected int codigo_t_num;

	/**
	 * Código a ser associado com tokens do tipo CADEIA
	 */
	protected int codigo_t_cadeia;

	public Map<String, Token> getTabela() {
		return palavrasReservadas;
	}

	@SuppressWarnings("unused")
	private TabelaDeTokens() {

	}

	public TabelaDeTokens(File arquivoDePalavrasReservadas) throws UnknownException {

		BufferedReader br = null;
		String line = null;
		String lexema, tipo, nome;
		StringTokenizer st = null;
		Token token = null;

		try {
			br = new BufferedReader(new FileReader(arquivoDePalavrasReservadas));

			while ((line = br.readLine()) != null) {
				if (line.startsWith("#")) {
					continue;
				}

				st = new StringTokenizer(line);

				lexema = st.nextToken();
				tipo = st.nextToken();
				nome = st.nextToken();

				token = new Token(lexema, tipo, nome, true);
				token.setCodigo(codigo);

				palavrasReservadas.put(lexema, token);

				codigo++;
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
			throw new UnknownException("Erro! Arquivo de tokens da linguagem não encontrado: "
					+ arquivoDePalavrasReservadas.getAbsolutePath(), e);
		} catch (IOException e) {
			e.printStackTrace();
			throw new UnknownException(e.getMessage(), e);
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
					throw new UnknownException(e.getMessage(), e);
				}
			}
		}

		this.codigo_t_ident = codigo++;
		this.codigo_t_cadeia = codigo++;
		this.codigo_t_num = codigo++;

	}

	/**
	 * Preenche os atributos restantes do <code>token</code>.
	 * 
	 * @param token
	 *            Token a ser preenchido os atribudos.
	 */
	public void resolverToken(Token token) {

		if (token == null) {
			return;
		}

		// Verificando se o token é uma palavra reservada
		Token tokenPalavraReservada = this.palavrasReservadas.get(token.getLexema());
		if (tokenPalavraReservada != null) {
			token.setCodigo(tokenPalavraReservada.getCodigo());
			token.setNome(tokenPalavraReservada.getNome());
			token.setPalavraReservada(true);
			return;
		}

		// Verificando se o token é um IDENTIFICADOR já identificado
		// anteriormente
		Token tokenIdentificadores = this.identificadores.get(token.getLexema());
		if (tokenIdentificadores != null) {
			token.setCodigo(tokenIdentificadores.getCodigo());
			token.setNome(tokenIdentificadores.getNome());
			token.setPalavraReservada(false);
			return;
		}

		// Token novo encontrado. Clonar e adicionar na tabela de
		// IDENTIFICADORES
		tokenIdentificadores = token.clone();
		tokenIdentificadores.setNome(tokenIdentificadores.getLexema());
		tokenIdentificadores.setPalavraReservada(false);
		if (tokenIdentificadores.ehIdentificador()) {
			tokenIdentificadores.setCodigo(this.codigo_t_ident);
		} else if (token.ehNumero()) {
			tokenIdentificadores.setCodigo(this.codigo_t_num);
		} else if (token.ehCadeia()) {
			tokenIdentificadores.setCodigo(this.codigo_t_cadeia);
		}

		token.setCodigo(tokenIdentificadores.getCodigo());
		token.setNome(tokenIdentificadores.getNome());
		token.setPalavraReservada(false);

		this.identificadores.put(tokenIdentificadores.getLexema(), token);

	}

}
